#ifndef _ROOM_
#define _ROOM_

#include"util.hpp"
#include"online_user.hpp"
#include"user_table.hpp"

#define BOARD_ROW 15
#define BOARD_COL 15
#define CHESS_WHITE 1
#define CHESS_BLACK 2

#define ROOM_TIMEOUT 5000
#define ROOM_FOREVER -1

class Room;

using room_ptr=std::shared_ptr<Room>;

enum room_statu
{
    GAME_START,GAME_OVER,GAME_RECONNECT
};

class Room
{
public:
    Room(uint64_t room_id,User_Table* tb_user,Online_Manager* online_user,wsserver_t* server)
    :_room_id(room_id),_statu(GAME_START),_player_count(0),_tb_user(tb_user),_server(server),
    _online_user(online_user),_board(BOARD_ROW,std::vector<int>(BOARD_COL,0))
    {
        INF_LOG("%lu 房间创建成功",_room_id);
    }

    // 获取房间id
    uint64_t id(){return _room_id;}

    // 获取房间状态
    room_statu statu(){return _statu;}

    void cancel_tp(){_tp->cancel();}

    //设置房间状态
    void set_statu(const room_statu &statu){_statu=statu;}

    // 获取房间人数
    int player_count(){return _player_count;}

    // 添加白棋玩家
    void add_white_user(uint64_t uid){_white_id=uid;_player_count++;}

    // 添加黑棋玩家
    void add_black_user(uint64_t uid){_black_id=uid;_player_count++;}

    // 获取白棋玩家id
    uint64_t get_white_user(){return _white_id;}

    // 获取黑棋玩家id
    uint64_t get_black_user(){return _black_id;}

    ~Room()
    {
        INF_LOG("%lu 房间销毁成功",_room_id);
    }
public:
    // 下棋的处理函数
    Json::Value handle_chess(Json::Value &req)
    {
        Json::Value resp=req;

        // 获取下棋位置和用户id
        int chess_row=req["row"].asInt();
        int chess_col=req["col"].asInt();
        uint64_t cur_uid=req["uid"].asInt64();

        // 判断房间内用户是否在线
        if(_online_user->in_game_room(_white_id)==false)
        {
            resp["result"]=true;
            resp["reason"]="对方掉线";
            resp["winner"]=(Json::UInt64)_black_id;
            return resp;
        }
        if(_online_user->in_game_room(_black_id)==false)
        {
            resp["result"]=true;
            resp["reason"]="对方掉线";
            resp["winner"]=(Json::UInt64)_white_id;
            return resp;
        }

        // 判断棋子黑白，并下棋
        int cur_color=(cur_uid==_white_id?CHESS_WHITE:CHESS_BLACK);
        _board[chess_row][chess_col]=cur_color;

        // 判断下棋之后是否存在获胜方，如果存在，返回获胜信息
        uint64_t winner_id=cheack_win(chess_row,chess_col,cur_color);
        if(winner_id!=0)
        {
            resp["reason"]="五行连珠";
            resp["result"]=true;
            resp["winner"]=(Json::UInt64)winner_id;
            return resp;
        }
        return resp;
    }

    // 退出房间的处理函数
    void handle_exit(uint64_t uid)
    {
        // DBG_LOG("handle_exit begin");
        Json::Value resp;
        if(_statu==GAME_START)
        {
            _statu=GAME_RECONNECT;
            DBG_LOG("handle_exit GAME_RECONNECT111");
            _tp=_server->set_timer(ROOM_TIMEOUT,std::bind(&Room::handle_reconnect,this,uid));
            resp["optype"]="put_chess";
            resp["result"]=true;
            resp["reason"]="对方掉线,等待重连中。。。";
            std::string body;
            Json_Util::serialize(resp,body);
            wsserver_t::connection_ptr conn;
            DBG_LOG("handle_exit GAME_RECONNECT222");
            _online_user->get_conn_game_room(uid==_white_id?_black_id:_white_id,conn);
            conn->send(body);
            // DBG_LOG("handle_exit GAME_RECONNECT333");
        }
    }

    void handle_reconnect(uint64_t uid)
    {
        // DBG_LOG("handle_reconnect begin");
        Json::Value resp;
        // 如果玩家是游戏进行时退出，并且重连超时，则判断胜者
        if(_statu==GAME_RECONNECT)
        {
            uint64_t winner_id=(uid==_white_id?_black_id:_white_id);
            resp["optype"]="put_chess";
            resp["result"]=true;
            resp["reason"]="对方掉线,我方获胜";
            resp["room_id"]=(Json::UInt64)_room_id;
            resp["uid"]=(Json::UInt64)uid;
            resp["row"]=-1;
            resp["col"]=-1;
            resp["winner"]=(Json::UInt64)winner_id;

            uint64_t loser_id=(winner_id==_white_id?_black_id:_white_id);
            _tb_user->win(winner_id);
            _tb_user->lose(loser_id);
            _statu=GAME_OVER;
            broadcast(resp);
            // DBG_LOG("handle_reconnect GAME_OVER");
        }
        else if(_statu==GAME_START)
        {
            resp["optype"]="put_chess";
            resp["result"]=true;
            resp["reason"]="对方重连，游戏继续";
            broadcast(resp);
            _player_count++;
            // DBG_LOG("handle_reconnect GAME_START");
            return;
        }
        _player_count--;
    }

    // 房间内广播函数
    void broadcast(Json::Value &resp)
    {
        // DBG_LOG("broadcast begin");
        std::string body;
        Json_Util::serialize(resp,body);

        // 获取黑白方玩家连接，之后判断玩家是否在线
        // if(_statu!=GAME_RECONNECT)
        // {
            wsserver_t::connection_ptr wconn, bconn;
            _online_user->get_conn_game_room(_white_id, wconn);
            if (wconn.get() == nullptr)
            {
                ERR_LOG("white user connect failed!");
            }
            _online_user->get_conn_game_room(_black_id, bconn);
            if (bconn.get() == nullptr)
            {
                ERR_LOG("black user connect failed!");
            }

            // 将信息发送给黑白两方
            wconn->send(body);
            bconn->send(body);
        // }
    }

    // 房间的处理响应函数
    void handle_request(Json::Value &req)
    {
        Json::Value resp;
        uint64_t room_id=req["room_id"].asUInt64();
        if(room_id!=_room_id)
        {
            resp["optype"]=req["optype"].asString();
            resp["result"]=false;
            resp["reason"]="房间号不匹配";
            return broadcast(resp);
        }

        // 信息类型是下棋
        if(req["optype"].asString()=="put_chess")
        {
            // 将信息转为下棋处理函数
            resp=handle_chess(req);
            // 判断是否有获胜者
            if(resp["winner"].asUInt64()!=0)
            {
                // 通过获胜方，处理两边玩家积分信息
                uint64_t winner_id=resp["winner"].asUInt64();
                uint64_t loser_id=(winner_id==_white_id?_black_id:_white_id);
                _tb_user->win(winner_id);
                _tb_user->lose(loser_id);
                _statu=GAME_OVER;
            }
        }
        // 信息类型是聊天
        else if(req["optype"].asString()=="chat")
        {
            // 返回发送用户信息和内容
            resp["optype"]=req["optype"].asString();
            resp["result"]=true;
            resp["message"]=req["message"].asString();
            resp["uid"]=req["uid"].asUInt64();
        }
        else
        {
            resp["optype"]=req["optype"].asString();
            resp["result"]=false;
            resp["reason"]="未知类型请求";
        }

        // 进行房间内广播
        return broadcast(resp);
    }
private:
    // 判断是否存在五子连珠
    // 通过行列位置，和偏移量判断
    bool five(int row,int col,int row_off,int col_off,int color)
    {
        int count=1;
        int search_row=row+row_off;
        int search_col=col+col_off;
        while(search_row>=0&&search_row<BOARD_ROW&&
              search_col>=0&&search_col<BOARD_COL&&
              _board[search_row][search_col]==color)
            {
                count++;
                search_row+=row_off;
                search_col+=col_off;
            }
        search_row=row-row_off;
        search_col=col-col_off;
        while(search_row>=0&&search_row<BOARD_ROW&&
              search_col>=0&&search_col<BOARD_COL&&
              _board[search_row][search_col]==color)
            {
                count++;
                search_row-=row_off;
                search_col-=col_off;
            }
        return (count>=5);
    }

    // 判断是否存在获胜方
    uint64_t cheack_win(int row,int col,int color)
    {
        if(five(row,col,0,1,color)||
           five(row,col,1,0,color)||
           five(row,col,0,-1,color)||
           five(row,col,-1,0,color))
           {
            return color==CHESS_WHITE?_white_id:_black_id;
           }
        return 0;
    }
private:
    uint64_t _room_id;                      //房间id
    room_statu _statu;                      //房间状态
    int _player_count;                      //房间人数
    uint64_t _white_id;                     //白棋用户id
    uint64_t _black_id;                     //黑棋用户id
    User_Table* _tb_user;
    Online_Manager* _online_user;
    std::vector<std::vector<int>> _board;   //棋盘

    wsserver_t* _server;
    wsserver_t::timer_ptr _tp;              //room关联的定时器
};

class Room_Manager
{
public:
    Room_Manager(User_Table* tb_user,Online_Manager* online_user,wsserver_t* server)
    :_rid(1),_tb_user(tb_user),_online_user(online_user),_server(server)
    {
        INF_LOG("Room_Manager init");
    }

    // 创建房间
    room_ptr create_room(uint64_t uid1,uint64_t uid2)
    {
        // 判断用户是否在大厅
        if(_online_user->in_game_hall(uid1)==false||_online_user->in_game_hall(uid2)==false)
        {
            ERR_LOG("user not in the hall");
            return room_ptr();
        }

        // 创建房间，并将用户加入房间中
        std::unique_lock<std::mutex> lock(_mutex);
        room_ptr rp(new Room(_rid,_tb_user,_online_user,_server));
        rp->add_white_user(uid1);
        rp->add_black_user(uid2);

        _rooms.insert(std::make_pair(_rid,rp));
        _users.insert(std::make_pair(uid1,_rid));
        _users.insert(std::make_pair(uid2,_rid));

        _rid++;
        return rp;
    }

    // 通过rid找到房间
    room_ptr get_room_by_rid(uint64_t rid)
    {
        std::unique_lock<std::mutex> lock(_mutex);
        auto it=_rooms.find(rid);
        if(it==_rooms.end())
        {
            return room_ptr();
        }
        return it->second;
    }

    // 通过uid找到房间
    room_ptr get_room_by_uid(uint64_t uid)
    {
        std::unique_lock<std::mutex> lock(_mutex);
        auto it = _users.find(uid);
        if (it == _users.end())
        {
            return room_ptr();
        }
        uint64_t rid=it->second;
        auto rit=_rooms.find(rid);
        if(rit==_rooms.end())
        {
            return room_ptr();
        }
        return rit->second;
    }

    // 删除房间
    void remove_room(uint64_t rid)
    {
        room_ptr rp=get_room_by_rid(rid);
        if(rp.get()==nullptr)
        {
            return;
        }

        uint64_t uid1=rp->get_white_user();
        uint64_t uid2=rp->get_black_user();

        std::unique_lock<std::mutex> lock(_mutex);
        _users.erase(uid1);
        _users.erase(uid2);
        _rooms.erase(rid);
    }

    // 删除房间中的玩家
    void remove_room_user(uint64_t uid)
    {
        room_ptr rp=get_room_by_uid(uid);
        if(rp.get()==nullptr)
        {
            return;
        }

        rp->handle_exit(uid);

        // 当房间中的玩家为0时，删除房间
        if(rp->player_count()==0)
        {
            remove_room(rp->id());
        }
    }

    ~Room_Manager()
    {
        INF_LOG("Room_Manager destroy");
    }
private:
    uint64_t _rid;               //下一个创建的房间id
    std::mutex _mutex;
    User_Table* _tb_user;
    Online_Manager* _online_user;
    std::unordered_map<uint64_t,room_ptr> _rooms;
    std::unordered_map<uint64_t,uint64_t> _users;

    wsserver_t* _server;
};

#endif